/**
 * @file led_drv_ws2812b.c
 * @author Jason Wen (embediot@163.com)
 * @brief WS2812B RGB灯珠驱动接口
 * @version 0.1
 * @date 2024-06-15
 * 
 * @copyright EmbedIoT Studio Copyright (c) 2024
 * 
 */

#include "led_drv_ws2812b.h"
#include "stm32f10x.h"

//定义一个 ws2812b rgb 灯珠驱动实例
led_drv_ws2812b_t led_drv_ws2812b;

#define DATA_PIN_HIGH()    GPIO_SetBits(GPIOA, GPIO_PIN_13)
#define DATA_PIN_LOW()     GPIO_ResetBits(GPIOA, GPIO_PIN_13)

static void delay_ns(unsigned int ns)
{
    while(ns--){
        __nop();
    }
}

static void ws2812b_reset(void)
{
    DATA_PIN_LOW(); delay_ns(1000);
}

static void ws2812b_t0(void)
{
    DATA_PIN_HIGH(); delay_ns(7);
    DATA_PIN_LOW(); delay_ns(26);
}

static void ws2812b_t1(void)
{
    DATA_PIN_HIGH(); delay_ns(26);
    DATA_PIN_LOW(); delay_ns(7);
}

static ws2812b_set_one_rgb(unsigned char *data, unsigned char brightness)
{
    unsigned char i = 0, j = 0;
    unsigned char c_temp = 0;
    unsigned int i_temp = 0;
    
    for (j = 0; j < 3; j++)
    {
        i_temp =  data[j] * brightness / 255;
        c_temp = (unsigned char)i_temp;
        for (i = 0; i < 8; i++)
        {   
            if (c_temp & 0x80)ws2812b_t1();
            else ws2812b_t0();

            c_temp <<= 1;
        }
    }
}

//把RGB和亮度数据，发送到ws2812b的数据线上
static void ws2812b_set_data(led_drv_ws2812b_t *ws2812b)
{
    unsigned int i = 0, j = 0, k = 0;;
    unsigned char color_buff[3];
    unsigned char brightness = 0;

    unsigned int led_count = ws2812b->drv.led_count;    //灯珠总数

    if(led_count == 0)return;

    __disable_irq();
    ws2812b_reset();

    for(i = 0;i < LED_MAX_NUM; i++)
    {
        color_buff[0] = ws2812b->rgbcw_value[i].green;
        color_buff[1] = ws2812b->rgbcw_value[i].red;
        color_buff[2] = ws2812b->rgbcw_value[i].blue;
        brightness = ws2812b->led_brightness[i];

        ws2812b_set_one_rgb(color_buff, brightness);
    }

    ws2812b_reset();
    __enable_irq(); 
}

static void ws2812b_gpio_init(void)
{
    GPIO_InitType GPIO_InitStructure;

	RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
	RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
	GPIO_InitStruct(&GPIO_InitStructure);

    GPIO_InitStructure.Pin = GPIO_PIN_15;// WS2812b驱动IO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
}

//////////////////////////////////////////////////////////////////////////

static int _init(void *_this)
{
    int ret = 0;

    ws2812b_gpio_init();    //初始化ws2812b的GPIO驱动

    return ret;
}

static int _turn_on(void *_this, led_id_t id)
{
    int ret = 0;

    if(id >= LED_MAX_NUM)return -1;

    led_drv_ws2812b_t *ws2812b = (led_drv_ws2812b_t *)_this;

    ws2812b->led_brightness[id] = ws2812b->old_led_brightness[id];    //用关灯前的亮度进行开灯

    ws2812b_set_data(ws2812b);

    return ret;
}

static int _turn_off(void *_this, led_id_t id)
{
    int ret = 0;

    if(id >= LED_MAX_NUM)return -1;

    led_drv_ws2812b_t *ws2812b = (led_drv_ws2812b_t *)_this;

    ws2812b->old_led_brightness[id] = ws2812b->led_brightness[id];    //关灯前保存亮度，开灯时使用

    ws2812b->led_brightness[id] = 0;

    ws2812b_set_data(ws2812b);

    return ret;
}

static int _set_brightness(void *_this, led_id_t id, unsigned char brightness)
{
    int ret = 0;

    if(id >= LED_MAX_NUM)return -1;

    led_drv_ws2812b_t *ws2812b = (led_drv_ws2812b_t *)_this;

    ws2812b->led_brightness[id] = brightness;
    ws2812b->old_led_brightness[id] = brightness;
    
    ws2812b_set_data(ws2812b);

    return ret;
}

static int _set_color(void *_this, led_id_t id, rgbcw_t *color)
{
    int ret = 0;

    if(id >= LED_MAX_NUM)return -1;

    led_drv_ws2812b_t *ws2812b = (led_drv_ws2812b_t *)_this;

    ws2812b->rgbcw_value[id].red = color->red;
    ws2812b->rgbcw_value[id].green = color->green;
    ws2812b->rgbcw_value[id].blue = color->blue;
    ws2812b->rgbcw_value[id].cold_w = color->cold_w;
    ws2812b->rgbcw_value[id].white = color->white;

    ws2812b_set_data(ws2812b);

    return ret;
}

static int _toggle(void *_this, led_id_t id)
{
    int ret = 0;

    if(id >= LED_MAX_NUM)return -1;

	led_drv_ws2812b_t *ws2812b = (led_drv_ws2812b_t *)_this;
	
    if(ws2812b->led_brightness[id] > 0)_turn_off(_this, id);
    else _turn_on(_this, id);

    return ret;
}


led_drv_ws2812b_t *led_drv_ws2812b_create(void)
{
    //初始化驱动函数接口
    led_drv_ws2812b.drv.init = _init;
    led_drv_ws2812b.drv.turn_on = _turn_on;
    led_drv_ws2812b.drv.turn_off = _turn_off;
    led_drv_ws2812b.drv.toggle = _toggle;
    led_drv_ws2812b.drv.set_brightness = _set_brightness;
    led_drv_ws2812b.drv.set_color = _set_color;
    led_drv_ws2812b.flush_data = ws2812b_set_data;

    led_drv_ws2812b.drv.led_count = LED_MAX_NUM;
    
    return &led_drv_ws2812b;
}
